#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

extern Log lg;

//version 4 -- 线程池版
//提前预先创建线程，不提供长服务。限定线程上线，把客户的访问作为任务
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5;//一般不要设置的太大

enum {
    UsageError = 1,
    SocketError,
    BindError,
    ListenError 
};

class TcpServer {
public:
    TcpServer(const uint16_t& port, const std::string& ip = defaultip) 
        : listensockfd_(defaultfd), port_(port), ip_(ip) {
    }

    void InitServer() {
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensockfd_ < 0) {
            lg(Fatal, "create socket error, errno:%d, errstring:%s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, listensockfd_:%d", listensockfd_);
        
        //防止偶发性的服务器无法立即重启
        int opt = 1;
        setsockopt(listensockfd_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &local.sin_addr);
        // local.sin_addr.s_addr = INADDR_ANY; //虚拟机使用

        if (bind(listensockfd_, (struct sockaddr*)&local, sizeof(local)) < 0) {
            lg(Fatal, "bind error, errno:%d, errstring:%s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind success, listensockfd_:%d", listensockfd_);

        //TCP是面向连接的，服务器一般是比较"被动的"。服务器始终处于一直在等待连接到来的状态
        if (listen(listensockfd_, backlog) < 0) {
            lg(Fatal, "listen error, errno:%d, errstring:%s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen success, listensockfd_:%d", listensockfd_);

    }

    void Start() {
        Daemon();
        // signal(SIGPIPE, SIG_IGN);//看Task.hpp run函数中写入的解释
        ThreadPool<Task>::GetInstance()->Start();//获取线程池单例
        // signal(SIGCHLD, SIG_IGN);//这种方式也可以不用等线程退出
        lg(Info, "TCP Server is running...");
        for(;;) {
            //1.获取新链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            //listensockfd_ 用于监听客户端的连接请求，它只是等待新的连接到来，并不直接参与数据的发送和接收。(一般只有一个)；
            //sockfd 用于与每个具体的客户端进行数据通信，代表了与某一个客户端的连接，服务器可以通过它来发送和接收数据。(可以有多个)
            int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
            if (sockfd < 0) {
                lg(Warning, "accept error, errno:%d, errstring:%s", errno, strerror(errno));
                continue;//获取失败，继续获取
            }

            // 为什么IP地址和端口号要主机和网络序列互转，这两个字段要写入操作系统，需要自己转。
            // 但我们发的信息为什么不需要？套接字接口会自动转换。
            // 因为使用socket通信，所以需要IP和端口号来标识自身唯一性。所以服务器才能给客户端发消息。
            // 主机上有各种APP，需要不同的端口号。如果固定端口号，可能会造成冲突。
            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &client.sin_addr, clientip, sizeof(clientip));
            
            //2.根据新连接来进行通信
            // 每有一个客户，就要创建一个线程。目前的Service是死循环，客户不退线程越来越多。来了连接才创建线程。
            lg(Info, "get a new link..., sockfd:%d, client ip:%s, client port:%d", sockfd, clientip, clientport);
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);

        }
    }

    ~TcpServer() {}
private:
    int listensockfd_;
    uint16_t port_;
    std::string ip_;
};



// //version 3 -- 多线程版
// const int defaultfd = -1;
// const std::string defaultip = "0.0.0.0";
// const int backlog = 5;//一般不要设置的太大
// Log lg;

// enum {
//     UsageError = 1,
//     SocketError,
//     BindError,
//     ListenError 
// };

// class TcpServer;

// class ThreadData {
// public:
//     ThreadData(int fd, const std::string& ip, const uint16_t& p, TcpServer* t)
//         : sockfd(fd), clientip(ip), clientport(p), tsvr(t) {}
// public:
//     int sockfd;
//     std::string clientip;
//     uint16_t clientport;
//     TcpServer* tsvr;
// };

// class TcpServer {
// public:
//     TcpServer(const uint16_t& port, const std::string& ip = defaultip) 
//         : listensockfd_(defaultfd), port_(port), ip_(ip) {
//     }

//     void InitServer() {
//         listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
//         if (listensockfd_ < 0) {
//             lg(Fatal, "create socket error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(SocketError);
//         }
//         lg(Info, "create socket success, listensockfd_:%d", listensockfd_);
        
//         struct sockaddr_in local;
//         memset(&local, 0, sizeof(local));
//         local.sin_family = AF_INET;
//         local.sin_port = htons(port_);
//         inet_aton(ip_.c_str(), &local.sin_addr);
//         // local.sin_addr.s_addr = INADDR_ANY; //虚拟机使用

//         if (bind(listensockfd_, (struct sockaddr*)&local, sizeof(local)) < 0) {
//             lg(Fatal, "bind error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(BindError);
//         }
//         lg(Info, "bind success, listensockfd_:%d", listensockfd_);

//         //TCP是面向连接的，服务器一般是比较"被动的"。服务器始终处于一直在等待连接到来的状态
//         if (listen(listensockfd_, backlog) < 0) {
//             lg(Fatal, "listen error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(ListenError);
//         }
//         lg(Info, "listen success, listensockfd_:%d", listensockfd_);

//     }

//     static void* Routine(void* args) { //成员函数还有this参数，和线程执行函数只有一个参数的规则不符，所以加static修饰
//         //如果Start函数中等待线程退出，那么无法并行。所以线程分离，运行完自动回收资源并退出。
//         pthread_detach(pthread_self());
//         ThreadData* td = static_cast<ThreadData*>(args);
//         td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
//         delete td;
//         return nullptr;
//     }
//     void Start() {
//         // signal(SIGCHLD, SIG_IGN);//这种方式可以不用等
//         lg(Info, "TCP Server is running...");
//         for(;;) {
//             //1.获取新链接
//             struct sockaddr_in client;
//             socklen_t len = sizeof(client);
//             //listensockfd_ 用于监听客户端的连接请求，它只是等待新的连接到来，并不直接参与数据的发送和接收。(一般只有一个)；
//             //sockfd 用于与每个具体的客户端进行数据通信，代表了与某一个客户端的连接，服务器可以通过它来发送和接收数据。(可以有多个)
//             int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
//             if (sockfd < 0) {
//                 lg(Warning, "accept error, errno:%d, errstring:%s", errno, strerror(errno));
//                 continue;//获取失败，继续获取
//             }

//             // 为什么IP地址和端口号要主机和网络序列互转，这两个字段要写入操作系统，需要自己转。
//             // 但我们发的信息为什么不需要？套接字接口会自动转换。
//             // 因为使用socket通信，所以需要IP和端口号来标识自身唯一性。所以服务器才能给客户端发消息。
//             // 主机上有各种APP，需要不同的端口号。如果固定端口号，可能会造成冲突。
//             uint16_t clientport = ntohs(client.sin_port);
//             char clientip[32];
//             inet_ntop(AF_INET, &client.sin_addr, clientip, sizeof(clientip));
            
//             //2.根据新连接来进行通信
//             // 每有一个客户，就要创建一个线程。目前的Service是死循环，客户不退线程越来越多。来了连接才创建线程。
//             lg(Info, "get a new link..., sockfd:%d, client ip:%s, client port:%d", sockfd, clientip, clientport);
//             ThreadData* td = new ThreadData(sockfd, clientip, clientport, this);
//             pthread_t pid;
//             pthread_create(&pid, nullptr, Routine, td);
//         }
//     }
//     void Service(int sockfd, const std::string& clientip, const uint16_t& clientport) {
//         //测试代码，发什么回显什么
//         char buffer[4096];
//         while (true) {
//             ssize_t n = read(sockfd, buffer, sizeof(buffer));
//             if (n > 0) {
//                 buffer[n] = 0;
//                 std::cout << "client say#" << buffer << std::endl;
//                 std::string echo_string = "TCP Server echo#";
//                 echo_string += buffer;

//                 write(sockfd, echo_string.c_str(), echo_string.size());
//             }
//             else if (n == 0) { //客户端退出
//                 lg(Info, "%s:%d quit, server close sockfd:%d", clientip.c_str(), clientport, sockfd);
//                 break;
//             }
//             else {
//                 lg(Warning, "read error, sockfd:%d, client ip:%s, client port:%d", sockfd, clientip.c_str(), clientport);
//                 break;
//             }
//         }
//     }

//     ~TcpServer() {

//     }
// private:
//     int listensockfd_;
//     uint16_t port_;
//     std::string ip_;
// };



// const int defaultfd = -1;
// const std::string defaultip = "0.0.0.0";
// const int backlog = 5;//一般不要设置的太大
// Log lg;

// enum {
//     UsageError = 1,
//     SocketError,
//     BindError,
//     ListenError 
// };

// class TcpServer {
// public:
//     TcpServer(const uint16_t& port, const std::string& ip = defaultip) 
//         : listensockfd_(defaultfd), port_(port), ip_(ip) {
//     }

//     void InitServer() {
//         listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
//         if (listensockfd_ < 0) {
//             lg(Fatal, "create socket error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(SocketError);
//         }
//         lg(Info, "create socket success, listensockfd_:%d", listensockfd_);
        
//         struct sockaddr_in local;
//         memset(&local, 0, sizeof(local));
//         local.sin_family = AF_INET;
//         local.sin_port = htons(port_);
//         inet_aton(ip_.c_str(), &local.sin_addr);
//         // local.sin_addr.s_addr = INADDR_ANY; //虚拟机使用

//         if (bind(listensockfd_, (struct sockaddr*)&local, sizeof(local)) < 0) {
//             lg(Fatal, "bind error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(BindError);
//         }
//         lg(Info, "bind success, listensockfd_:%d", listensockfd_);

//         //TCP是面向连接的，服务器一般是比较"被动的"。服务器始终处于一直在等待连接到来的状态
//         if (listen(listensockfd_, backlog) < 0) {
//             lg(Fatal, "listen error, errno:%d, errstring:%s", errno, strerror(errno));
//             exit(ListenError);
//         }
//         lg(Info, "listen success, listensockfd_:%d", listensockfd_);

//     }

//     void Start() {
//         // signal(SIGCHLD, SIG_IGN);//这种方式可以不用等
//         lg(Info, "TCP Server is running...");
//         for(;;) {
//             //1.获取新链接
//             struct sockaddr_in client;
//             socklen_t len = sizeof(client);
//             //listensockfd_ 用于监听客户端的连接请求，它只是等待新的连接到来，并不直接参与数据的发送和接收。(一般只有一个)；
//             //sockfd 用于与每个具体的客户端进行数据通信，代表了与某一个客户端的连接，服务器可以通过它来发送和接收数据。(可以有多个)
//             int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
//             if (sockfd < 0) {
//                 lg(Warning, "accept error, errno:%d, errstring:%s", errno, strerror(errno));
//                 continue;//获取失败，继续获取
//             }

//             // 为什么IP地址和端口号要主机和网络序列互转，这两个字段要写入操作系统，需要自己转。
//             // 但我们发的信息为什么不需要？套接字接口会自动转换。
//             // 因为使用socket通信，所以需要IP和端口号来标识自身唯一性。所以服务器才能给客户端发消息。
//             // 主机上有各种APP，需要不同的端口号。如果固定端口号，可能会造成冲突。
//             uint16_t clientport = ntohs(client.sin_port);
//             char clientip[32];
//             inet_ntop(AF_INET, &client.sin_addr, clientip, sizeof(clientip));
            
//             //2.根据新连接来进行通信
//             lg(Info, "get a new link..., sockfd:%d, client ip:%s, client port:%d", sockfd, clientip, clientport);
            
//             // //version 1 -- 单进程版
//             // //启动2个客户端，只有第一个能正常发消息。第一个退了以后，第二个客户端的消息才发送。
//             // Service(sockfd, clientip, clientport);
//             // close(sockfd);

//             //version 2 -- 多进程版
//             pid_t id = fork();
//             if (id == 0) {
//                 //child
//                 close(listensockfd_); //子进程会拷贝父进程的文件描述表，关闭不需要的。
//                 //子进程成功创建孙子进程后退出，这样就能立马被父进程回收。孙子进程被系统领养
//                 if (fork() > 0) exit(0);
//                 Service(sockfd, clientip, clientport);
//                 close(sockfd);//通信结束，关闭该文件描述符。
//                 exit(0);
//             }
//             //father
//             close(sockfd);//和子进程一样的原因，关闭不需要的。
//             //如果父进程阻塞等待就和单进程版本一样，只有子进程退出了，父进程才能获取新连接。所以子进程要创建孙子进程
//             pid_t rid = waitpid(id, nullptr, 0);
//         }
//     }
//     void Service(int sockfd, const std::string& clientip, const uint16_t& clientport) {
//         //测试代码，发什么回显什么
//         char buffer[4096];
//         while (true) {
//             ssize_t n = read(sockfd, buffer, sizeof(buffer));
//             if (n > 0) {
//                 buffer[n] = 0;
//                 std::cout << "client say#" << buffer << std::endl;
//                 std::string echo_string = "TCP Server echo#";
//                 echo_string += buffer;

//                 write(sockfd, echo_string.c_str(), echo_string.size());
//             }
//             else if (n == 0) { //客户端退出
//                 lg(Info, "%s:%d quit, server close sockfd:%d", clientip.c_str(), clientport, sockfd);
//                 break;
//             }
//             else {
//                 lg(Warning, "read error, sockfd:%d, client ip:%s, client port:%d", sockfd, clientip.c_str(), clientport);
//                 break;
//             }
//         }
//     }

//     ~TcpServer() {

//     }
// private:
//     int listensockfd_;
//     uint16_t port_;
//     std::string ip_;
// };

